import { Request, Response, NextFunction, request } from 'express'
import { getUserByName } from '../user/user.service'
import bcrypt from 'bcrypt'
import jwt from 'jsonwebtoken'
import { PUBLIC_KEY } from '../app/app.config'
import type { TokenPayload } from './auth.interface'
import { possess } from './auth.service'

export const validateLogin = async (
  req: Request,
  res: Response,
  next: NextFunction
) => {
  const { name, password } = req.body

  if (!(name && password))
    return next(new Error('USER_NAME_OR_PASSWORD_IS_EMPTY'))

  const { id, password: realPassword } = await getUserByName(name)
  if (!(id && (await bcrypt.compare(password, realPassword))))
    return next(new Error('USER_LOGIN'))

  req.body.id = id

  next()
}

export const authGuard = async (
  req: Request,
  res: Response,
  next: NextFunction
) => {
  try {
    const authorization = req.headers.authorization
    if (!authorization) throw new Error()

    const token = authorization.replace('Bearer ', '')
    if (!token) throw new Error()

    const decoded = jwt.verify(token, PUBLIC_KEY, { algorithms: ['RS256'] })
    request.user = decoded as TokenPayload

    next()
  } catch (error) {
    next(new Error('UNAUTHENTICATED'))
  }
}

interface AccessControlOptions {
  possession?: boolean
}

export const accessControl = (options: AccessControlOptions) => {
  return async (req: Request, res: Response, next: NextFunction) => {
    const { possession } = options
    const { id: userId } = request.user

    if (userId === 1) return next()

    const resourceIdParam = Object.keys(req.params)[0]

    const resourceType = resourceIdParam.replace('Id', '')
    const resourceId = +req.params[resourceIdParam]

    if (!possession) return next()

    try {
      const ownRecource = await possess({ resourceId, resourceType, userId })

      if (!ownRecource) throw new Error()
      return next()
    } catch (error) {
      return next(new Error('UNPERMISSTION'))
    }
  }
}
